/*
 * Copyright (c) 2021 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <limits.h>
#include <signal.h>
#include <pthread.h>
#include <sys/types.h>

#include "http_req.h"
#include "http_svr.h"
#include "http_util.h"
#include "http_rapi.h"
#include "http_sse.h"
#include "lite_httpd.h"

/**
    http配置.
*/
#define SVR_PORT_DEF    8443 // server TCP port
#define SVR_CRT_FILE    "./key/server.crt"
#define SVR_KEY_FILE    "./key/server.key" // server priv key文件名
#define RSC_TAB_SIZE    64 // rsc table size.

/**
    http service type.
*/
typedef enum HttpSvcType {
    HST_HTTP = 0,
    HST_RAPI,
    HST_SSE,
    HST_SIZE
}   HttpSvcType;

/**
    resource node.
*/
typedef struct RscNode {
    const char* path; // url.path
    uint32_t methods; // methods codes

    HttpReqProc proc; // req handle
    void *user; // user proc request

    HttpReqExt ext; // 对象扩展处理器
    HttpSvcType svcType; // service type
    uint32_t flags; // HttpReq flags

    int chnFd; // user接收事件的channel
}   RscNode;

/**
    httpd datas.
*/
static pthread_mutex_t g_rscMutex; // 访问互斥，主要用于保护对rscTab的访问
static RscNode g_rscTab[RSC_TAB_SIZE]; // 资源表
static EvtMon *g_evtMon = NULL; // HttpSvr依赖的EvtMon
static HttpSvr *g_httpSvr = NULL; // LiteHttpd维护的HttpSvr实例
static pthread_t g_svcThrd = 0; // 驱动HttpSvr的线程ID

/*
    inner service 前向申明
*/
static int InnerWwwProc(void* user, HttpReq* req);
static void InnerSvcInit(void);
static void InnerSvcExit(void);

/**
    get file处理工具函数.
*/
int HttpdGetFile(HttpReq* req, const char* filePath, const char* ctt)
{
    char buf[INT_STR_BUF_SIZE];
    int res = -1;
    char stFilePath[PATH_MAX] = {0};

    if (!realpath(filePath, stFilePath)) {
        LOGE("HttpdGetFile filePath (%s) is not exit\n", filePath);
        HI_ASSERT(0);
    }
    LOGI("HttpdGetFile standard file path:%s\n", stFilePath);

    // try to open file
    FILE *file = fopen(stFilePath, "rb");
    HI_EXP_CMD_GOTO(!file, HttpReqSimpleRsp(req, "404"), END, "open '%s' FAIL\n", stFilePath);

    // get file size for verify on sending
    off_t fileSize = FileGetSize(file);
    HI_EXP_CMD_GOTO(fileSize < 0, HttpReqSimpleRsp(req, "500"), END, "get '%s' size FAIL\n", HttpReqReqPath(req));

    // write headers
    HttpHdrs *hdrs = HttpHdrsNew(0);
    HI_ASSERT(hdrs);
    HttpHdrsAdd(hdrs, HH_STATUS, -1, "200", -1);
    HttpHdrsAdd(hdrs, HH_CONTENT_TYPE, -1, ctt, -1);
    if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%jd", (intmax_t)fileSize) < 0) {
        HI_ASSERT(0);
    }
    HttpHdrsAdd(hdrs, HH_CONTENT_LENGTH, -1, buf, -1);
    HttpReqWriteHdrs(req, hdrs, false);
    HttpHdrsDelete(hdrs);

    // read file blocks and send via HttpReq one by one
    off_t total = 0;
    bool eof = false;
    while (!eof) {
        // read a block data
        MemBlk *blk = MemBlkNew(HUGE_BUF_SIZE); // ensure by xiao
        if (!blk) {
            HI_ASSERT(0);
        }
        int len = fread(blk->data, 1, blk->size, file);
        if (len <= 0) {
            MemBlkDelete(blk);
            if (len < 0) {
                LOGE("fread() FAIL, err='%s'\n", strerror(errno));
                break;
            }
            blk = NULL;
        } else {
            blk->len = len;
        }

        eof = feof(file);
        HI_ASSERT(len > 0 || eof);

        // write the block, 注意blk可能为NULL，这是正确的
        res = HttpReqWriteBody(req, blk, eof, false);
        if (res < 0) { // 常见原因: 连接断开，client reset
			LOGE("writeBody() FAIL, ret=%d\n", res);
            break;
        }
        // 校验total
        total += len;
        if (total > fileSize) {
            LOGE("total(%jd) > fileSize(%jd)\n", (intmax_t)total, (intmax_t)fileSize);
            HttpReqReset(req, -1);
            break;
        }
    }
    res = 0;

    END:
        if (file) {
            fclose(file);
        }
        return res;
}

/**
    put file处理工具函数.
*/
int HttpdPutFile(HttpReq* req, const char* filePath, bool reply)
{
    MemBlk *blk = NULL;
    const char* hval = NULL;
    FILE *file = NULL;
    int64_t cttLen;
    int64_t gotLen;
    int len;
    int ret = -1;
    char stFilePath[PATH_MAX] = {0};

    if (!realpath(filePath, stFilePath)) {
        LOGE("HttpPutFile filePath (%s) is not exit\n", filePath);
        HI_ASSERT(0);
    }
    LOGI("HttpdPutFile standard file path:%s\n", stFilePath);
    // try to open file
    file = fopen(stFilePath, "wb");
    if (!file) {
        LOGE("open '%s' FAIL\n", stFilePath);
        return -1;
    }

    hval = HttpHdrsFind(HttpReqReqHdrs(req), HH_CONTENT_LENGTH, NULL);
    cttLen = hval ? atoll(hval) : -1LL;

    // read file blocks and send via HttpReq one by one
    gotLen = 0;
    while ((ret = HttpReqReadBody(req, &blk, false)) > 0) {
        HI_ASSERT(blk);
        gotLen += blk->len;
        if (blk->len > 0 && (len = fwrite(blk->data, 1, blk->len, file)) != blk->len) {
            LOGE("fwrite FAIL, err='%s, %d'\n", strerror(errno), errno);
            MemBlkDelete(blk);
            ret = -1;
            goto END;
        }
        if (blk) {
            MemBlkDelete(blk);
        }
    }
    if (ret < 0) {
        LOGE("readBody FAIL, ret=%d\n", ret);
    } else if (cttLen >= 0 && cttLen != gotLen) {
        LOGE("size mismatch, cttLen=%jd, gotLen=%jd\n", cttLen, gotLen);
    }

    END:
        fclose(file);
        if (ret < 0) {
            LOGW("remove '%s' for proc FAIL\n", stFilePath);
            int err = remove(stFilePath);
            HI_EXP_LOGE(err < 0, "remove '%s' FAIL, err='%s, %d'\n", stFilePath, strerror(errno), errno);
        }
        if (reply) {
            const char *status = ret < 0 ? "500" : "200";
            HttpReqSimpleRsp(req, status);
        }
        return ret;
}

/**
    供user使用的httpd event处理函数.
*/
void HttpdEvtProc(void* user, int fd, uint32_t evts)
{
    EvtChkRet(evts, FDE_IN, fd);

    // 循环读取HttpReq指针，并处理
    HttpdEvt evt = {0};
    while (FdReadMsg(fd, &evt, sizeof(evt)) == sizeof(evt)) {
        HI_ASSERT(evt.proc);
        evt.proc(evt.user, evt.req);
    }
}

/**
    精确匹配path.
*/
static RscNode* HttpdFindRsc(const char* path, uint32_t method, int* rscId)
{
    *rscId = -1;

    for (int i = 0; i < HI_ARRAY_SIZE(g_rscTab); i++) {
        RscNode *node = &g_rscTab[i];

        if (!node->path || strcmp(node->path, path) != 0) {
            continue;
        }

        if (!(node->methods & method)) {
            LOGW("path '%s' match, but method '%s' mismatch\n",
                path, HttpCodeToMethod(method));
            return NULL;
        }

        *rscId = i;
        return node;
    }
    return NULL;
}

/**
    收到RAPI的onBodyClose事件.
*/
static int HttpdOnRapi(void* user, HttpReq* req)
{
    HI_ASSERT(g_httpSvr);
    const char* path = HttpReqReqPath(req);
    RscNode *node = NULL;
    int rscId;
    int ret;

    // 加大锁
    MutexLock(&g_rscMutex);

    HttpReqGetAttachProc(req, NULL, NULL, &rscId);

    // 判断rscId对应的item是否匹配，有可能其已经被user注销了
    if (g_rscTab[rscId].path && strcmp(g_rscTab[rscId].path, path) == 0) {
        node = &g_rscTab[rscId];
    } else {
        uint32_t method = HttpMethodToCode(HttpReqReqMethod(req));
        if ((node = HttpdFindRsc(path, method, &rscId)) == NULL) {
            LOGE("req{%s, %s} FAIL, for rsc unreg\n", path, HttpReqReqMethod(req));
            HttpReqSimpleRsp(req, "404");
            ret = 0;
            goto END;
        }
    }

    // 分发
    HttpReqAddRef(req);
    if (node->chnFd < 0) { // 直接分发
        HI_ASSERT(node->proc);
        ret = node->proc(node->proc, req);
    } else { // 发送事件给chnFd
        HttpdEvt evt = {
            .req = req,
            .proc = node->proc,
            .user = node->user
        };
        if (FdWriteMsg(node->chnFd, &evt, sizeof(evt)) != sizeof(evt)) {
            LOGE("post evt to user FAIL\n");
            HttpReqDelRef(req);
        }
        ret = 0;
    }

    END:
        MutexUnlock(&g_rscMutex);
        return ret;
}

/**
    HttpReq分发.
    需要caller加锁，此函数不负责加锁。
*/
static int HttpdDispatch(HttpReq* req, const RscNode *node, int rscId)
{
    // 特殊处理rapi的分发
    if (node->svcType == HST_RAPI) {
        HttpReqSetAttachProc(req, NULL, NULL, rscId);
        HttpReqSetOnBodyEnd(req, HttpdOnRapi, NULL);
        HttpReqDelRef(req);
        return 0;
    }

    // 普通请求分发
    HI_ASSERT(node->proc);
    if (node->chnFd < 0) { // 直接回调
        node->proc(node->user, req);
    } else { // 发送事件给chnFd
        HttpdEvt evt = {
            .req = req,
            .proc = node->proc,
            .user = node->user
        };
        if (FdWriteMsg(node->chnFd, &evt, sizeof(evt)) != sizeof(evt)) {
            LOGE("post evt to user FAIL\n");
            HttpReqDelRef(req);
        }
    }
    return 0;
}

/**
    HttpReq复用分发器handler.
*/
static int HttpdMux(void* user, HttpReq* req)
{
    const char* path = HttpReqReqPath(req);
    uint32_t method = HttpMethodToCode(HttpReqReqMethod(req));
    int rscId = -1;
    int ret;

    // 加大锁
    MutexLock(&g_rscMutex);

    if (!path || !*path) {
        LOGE("HttpReq has no PATH, ERR\n");
        HttpReqSimpleRsp(req, "400");
        goto FAIL;
    }

    // 先精确匹配path
    const RscNode *node = HttpdFindRsc(path, method, &rscId);
    // 查找失败时，判断是否存为web GET请求
    if (!node) {
        if (method == HMC_GET) {
            InnerWwwProc(NULL, req);
            ret = 0;
            goto END;
        } else {
            LOGE("unkn req{%s, %s}\n", path, HttpReqReqMethod(req));
            HttpReqSimpleRsp(req, "404");
            goto FAIL;
        }
    }

    // 下面为精确匹配成功后的处理 ...
    // 根据user注册的svcType做HttpReq扩展处理
    if (node->ext && node->ext(req) < 0) {
        LOGE("HttpReq ext FAIL, path='%s'\n", path);
        HttpReqSimpleRsp(req, "500");
        goto FAIL;
    }

    // 设置flags
    if (node->flags) {
        HttpReqAddFlag(req, node->flags);
    }

    // 分发请求，其会delRef req
    ret = HttpdDispatch(req, node, rscId);
    goto END;

    FAIL:
        HttpReqDelRef(req);
        ret = -1;

    END:
        MutexUnlock(&g_rscMutex);
        return ret;
}

/**
    server thread.
*/
static void* HttpdThrd(void *user)
{
    uint16_t port = (uint16_t)(uintptr_t)user;
    int ret;

    HI_ASSERT(g_httpSvr);
    ret = HttpSvrStart(g_httpSvr, port, NULL, HttpdMux, NULL);
    if (ret < 0) {
        LOGE("start server FAIL, ret=%d\n", ret);
        return NULL;
    }

    EmRun(g_evtMon);

    HI_ASSERT(g_httpSvr);
    HttpSvrStop(g_httpSvr);
    return NULL;
}

/**
    注册处理器.
*/
static int HttpdReqAny(const char* path, const char* methods, const RscNode* nodeInfo)
{
    HI_ASSERT(g_httpSvr);
    int idleId = -1;
    int i;
    int ret;

    MutexLock(&g_rscMutex);

    // 判断path是否已经注册，并寻找空闲的node
    for (i = 0; i < sizeof(g_rscTab) / sizeof(*g_rscTab); i++) {
        if (idleId < 0 && !g_rscTab[i].path) {
            idleId = i;
        }
        if (g_rscTab[i].path && strcmp(g_rscTab[i].path, path) == 0) {
            LOGE("reg FAIL, for '%s' exist\n", path);
            ret = -1;
            goto END;
        }
    }

    // 寻找空闲的node
    for (; idleId < 0 && i < sizeof(g_rscTab) / sizeof(*g_rscTab); i++) {
        if (!g_rscTab[i].path) {
            idleId = i;
        }
    }
    if (idleId < 0) {
        LOGE("reg FAIL, for tab full\n");
        ret = -1;
        goto END;
    }

    // 注册
    HI_ASSERT(!g_rscTab[idleId].proc);
    g_rscTab[idleId] = *nodeInfo;
    g_rscTab[idleId].path = path; // 临时认为传入的是常量
    g_rscTab[idleId].methods = methods ? HttpMethodsToCodes(methods) : HMC_ALL;
    if (!g_rscTab[idleId].methods) {
        if (methods && strcmp(methods, "*") != 0) {
            LOGW("HTTPD: reg '%s' with unkn methods '%s', regard '*'\n", path, methods);
        }
        g_rscTab[idleId].methods = HMC_ALL;
    }
    ret = 0;

    END:
        MutexUnlock(&g_rscMutex);
        return ret;
}

/**
    注册http处理器.
*/
int HttpdRegHttp(const char* path, const char* methods,
    HttpReqProc proc, void* user, SkPair evtChn, uint32_t flags)
{
    RscNode info = {
        .proc = proc,
        .user = user,
        .ext = NULL,
        .svcType = HST_HTTP,
        .chnFd = evtChn.out,
        .flags = flags
    };
    return HttpdReqAny(path, methods, &info);
}

/**
    注册rapi处理器.
*/
int HttpdRegRapi(const char* path,
    HttpReqProc proc, void* user, SkPair evtChn)
{
    RscNode info = {
        .proc = proc,
        .user = user,
        .ext = RapiReqInit,
        .svcType = HST_RAPI,
        .chnFd = evtChn.out,
        .flags = 0
    };
    return HttpdReqAny(path, "POST", &info);
}

/*
    注册sse处理器
*/
int HttpdRegSse(const char* path,
    HttpReqProc proc, void* user, SkPair evtChn)
{
    RscNode info = {
        .proc = proc,
        .user = user,
        .ext = SseReqInit,
        .svcType = HST_SSE,
        .chnFd = evtChn.out,
        .flags = 0
    };
    return HttpdReqAny(path, "GET", &info);
}

/**
    注销任意处理器.
*/
int HttpdUnreg(const char* path)
{
    HI_ASSERT(g_httpSvr);
    int i;

    MutexLock(&g_rscMutex);

    // 判断path是否已经注册，并寻找空闲的node
    for (i = 0; i < HI_ARRAY_SIZE(g_rscTab); i++) {
        if (!g_rscTab[i].path) {
            continue;
        }

        if (strcmp(g_rscTab[i].path, path) == 0) {
            if (memset_s(&g_rscTab[i], sizeof(g_rscTab[i]), 0, sizeof(g_rscTab[i])) != EOK) {
                HI_ASSERT(0);
            }
            break;
        }
    }

    MutexUnlock(&g_rscMutex);
    return i < HI_ARRAY_SIZE(g_rscTab) ? 0 : -1;
}

/**
    httpd service init.
*/
int HttpdInit(uint16_t port)
{
    LOGI("HTTPD: init ...\n");
    HI_ASSERT(!g_evtMon && !g_svcThrd && !g_httpSvr);

    if (EmCreate(&g_evtMon) < 0) {
        HI_ASSERT(0);
    }
    if (HttpSvrCreate(&g_httpSvr, g_evtMon, SVR_CRT_FILE, SVR_KEY_FILE) < 0) {
        HI_ASSERT(0);
    }

    RecurMutexInit(&g_rscMutex);
    if (memset_s(&g_rscTab, sizeof(g_rscTab), 0, sizeof(g_rscTab)) != EOK) {
        HI_ASSERT(0);
    }

    if (port == 0 || port == 0xFFFF) {
        port = GetCfgInt("lite_httpd:svr_port", SVR_PORT_DEF);
    }
    if (pthread_create(&g_svcThrd, NULL, HttpdThrd, (void*)(uintptr_t)port) < 0) {
        HI_ASSERT(0);
    }

    InnerSvcInit();

    LOGI("HTTPD: init done\n");
    return 0;
}

/**
    httpd service exit.
*/
int HttpdExit(void)
{
    LOGI("HTTPD: exit ...\n");
    HI_ASSERT(g_httpSvr);

    InnerSvcExit();

    HI_ASSERT(g_evtMon && g_svcThrd);
    EmQuit(g_evtMon);
    pthread_join(g_svcThrd, NULL);
    g_svcThrd = 0;

    HI_ASSERT(g_httpSvr);
    HttpSvrDestroy(g_httpSvr);
    g_httpSvr = NULL;

    HI_ASSERT(g_evtMon);
    EmDestroy(g_evtMon);
    g_evtMon = NULL;

    MutexLock(&g_rscMutex);
    if (memset_s(&g_rscTab, sizeof(g_rscTab), 0, sizeof(g_rscTab)) != EOK) {
        HI_ASSERT(0);
    }
    MutexUnlock(&g_rscMutex);

    MutexDestroy(&g_rscMutex);
    if (memset_s(&g_rscMutex, sizeof(g_rscMutex), 0, sizeof(g_rscMutex)) != EOK) {
        HI_ASSERT(0);
    }

    LOGI("HTTPD: exit done\n");
    return 0;
}

/*************************************************************************************************
    inner/demo service
    随同httpd启动的内置服务，包括www服务和rapi/sse/post demo.
*************************************************************************************************/
#define SP_INNER_SVC

/**
    inner/demo service const.
*/
#define URL_RAPI_DEMO       "/rapi/demo"
#define URL_SSE_DEMO        "/sse/demo"
#define URL_POST_DEMO       "/post/demo"
#define URL_SPEED_TEST      "/post/speed_test"

#define WWW_ROOT_DIR        "/www"

/**
    inner/demo servcie var.
*/
static int g_svcThrdNum = 0; // 正在运行的后台服务线程数目

/**
    sse处理线程，产生事件并push.
*/
static void* SseDemoThrd(void *user)
{
    static const int evtNum = 5; // 发送的事件数目
    static const long waitIntval = 3; // 3s

    HttpReq *req = (HttpReq*)user;
    char data[TINY_BUF_SIZE];
    int id;
    int len;
    int res;

    for (id = 0, res = 0; res >= 0 && id < evtNum; id++) {
        sleep(waitIntval);
        len = snprintf_s(data, sizeof(data), sizeof(data) - 1, "evt#%d", id);
        res = SseReqPush(req, id, NULL, data, len);
    }

    SseReqClose(req, 0);
    HttpReqDelRef(req);
    if (__sync_sub_and_fetch(&g_svcThrdNum, 1) < 0) {
        HI_ASSERT(0);
    }
    return NULL;
}

/**
    sse handle demo.
*/
static int SseDemoProc(void* user, SseReq* req)
{
    pthread_t thrdId;
    int res;

    res = SseReqReply(req, 0);
    if (res < 0) {
        return res;
    }

    __sync_add_and_fetch(&g_svcThrdNum, 1);
    res = DetachThreadCreate(&thrdId, SseDemoThrd, req);
    HI_ASSERT(res >= 0);
    HI_ASSERT(thrdId);
    return 0;
}

/**
    当RapiReq收齐req body.
*/
static int RapiDemoProc(void* user, RapiReq* req)
{
    const char *inData;
    int inLen;

    inData = RapiReqInData(req, &inLen);
    // client可能没有发送request body，但其在RapiReqInit()已处理
    HI_ASSERT(inData);
    LOGI("HTTPD: on rapi, inLen=%d, inData=\n", inLen);
    DOLOGI(HiDumpTxt(inData, inLen));

    static const char outData[] = "{ \"hello\": \"rapi\" }";

    // echo back request body to client
    RapiReqReply(req, outData, sizeof(outData) - 1);
    HttpReqDelRef(req);
    return 0;
}

/**
    文件发送处理线程，处理单个文件.
*/
static void* PostDemoThrd(void *user)
{
    HttpReq *req = (HttpReq*)user;
    MemBlk *blk = NULL;
    HttpHdrs *hdrs = NULL;
    const char* hval = NULL;
    int64_t total = 0;
    int len;
    int res;

    hval = HttpHdrsFind(HttpReqReqHdrs(req), HH_CONTENT_LENGTH, NULL);
    len = hval ? atoi(hval) : -1;

    LOGI("HTTPD: on POST, len=%d(KB)\n", len / HI_KB);
    int64_t begTime = HiClockMs();
    // read file blocks and send via HttpReq one by one
    while ((res = HttpReqReadBody(req, &blk, false)) > 0) {
        HI_ASSERT(blk);
        total += blk->len;
        MemBlkDelete(blk);
    }

    int64_t cost = HiClockMs() - begTime;
    int64_t speed = total / cost * HI_MS_OF_SEC / HI_KB;
    LOGI("HTTPD: on POST done, len=%jd(KB), speed=%jd(KB/s), cost=%jd\n", total / HI_KB, speed, cost);

    // write headers
    hdrs = HttpHdrsNew(0);
    HI_ASSERT(hdrs);
    HttpHdrsAdd(hdrs, HH_STATUS, -1, "200", -1);
    HttpReqWriteHdrs(req, hdrs, true);
    HttpHdrsDelete(hdrs);

    HttpReqDelRef(req);
    if (__sync_sub_and_fetch(&g_svcThrdNum, 1) < 0) {
        HI_ASSERT(0);
    }
    return NULL;
}

/**
    post file proc.
*/
static int PostDemoProc(void* user, HttpReq* req)
{
    pthread_t thrdId;
    int ret;

    __sync_add_and_fetch(&g_svcThrdNum, 1);
    ret = DetachThreadCreate(&thrdId, PostDemoThrd, req);
    HI_ASSERT(ret == 0);
    return 0;
}

/**
    www get one file thread.
*/
static void* InnerWwwThrd(void *user)
{
    HttpReq *req = (HttpReq*)user;
    const char *ctt;
    char buf[PATH_MAX];

    // get file content-type by file ext name
    ctt = FileExtToCtt(HttpReqReqPath(req));
    if (!ctt) {
        ctt = "*/*";
    }
    LOGD("HTTPD: FileGet rsp ctt='%s'\n", ctt);

    if (snprintf_s(buf, sizeof(buf), sizeof(buf) - 1, "%s%s", WWW_ROOT_DIR, HttpReqReqPath(req)) < 0) {
        HI_ASSERT(0);
    }
    HttpdGetFile(req, buf, ctt);

    // sleep(10); // 增加处理耗时，for test only
    HttpReqDelRef(req);
    if (__sync_sub_and_fetch(&g_svcThrdNum, 1) < 0) {
        HI_ASSERT(0);
    }
    return NULL;
}

/**
    www get file proc.
*/
static int InnerWwwProc(void* user, HttpReq* req)
{
    pthread_t thrdId;
    int ret;

    __sync_add_and_fetch(&g_svcThrdNum, 1);
    ret = DetachThreadCreate(&thrdId, InnerWwwThrd, req);
    HI_ASSERT(ret == 0);
    return 0;
}

/**
    等待所有内置服务完成.
*/
static void InnerSvcWait(long timeout)
{
    static const long timeoutDef = 15000; // 默认等待s数
    static const long waitPerLoop = 100; // 循环中每次等待的ms数

    if (g_svcThrdNum == 0) {
        return;
    }
    HI_ASSERT(g_svcThrdNum >= 0);

    timeout = timeout < 0 ? timeoutDef : timeout;
    LOGI("HTTPD: %d inner svc exist, wait %ldms ...\n", g_svcThrdNum, timeout);

    int64_t begTime = HiClockMs();
    while (g_svcThrdNum > 0 && HiClockMs() - begTime < timeout) {
        usleep(waitPerLoop * HI_USLEEP_MS); // 每次等待100ms
    }

    if (g_svcThrdNum > 0) {
        LOGE("HTTPD: wait inner svc end TIMEOUT, left=%d\n", g_svcThrdNum);
    } else {
        LOGI("HTTPD: wait inner svc end done, cost=%lldms ...\n", (HiClockMs() - begTime));
    }
}

/**
    inner service init.
*/
static void InnerSvcInit(void)
{
    const SkPair nullSkPair = {
        .in = -1,
        .out = -1
    };
    HttpdRegRapi(URL_RAPI_DEMO, RapiDemoProc, NULL, nullSkPair);
    HttpdRegSse(URL_SSE_DEMO, SseDemoProc, NULL, nullSkPair);
    HttpdRegHttp(URL_POST_DEMO, "POST", PostDemoProc, NULL, nullSkPair, 0);
    HttpdRegHttp(URL_SPEED_TEST, "POST", PostDemoProc, NULL, nullSkPair, 0);
}

/**
    inner service exit.
*/
static void InnerSvcExit(void)
{
    HttpdUnreg(URL_RAPI_DEMO);
    HttpdUnreg(URL_SSE_DEMO);
    HttpdUnreg(URL_POST_DEMO);
    HttpdUnreg(URL_SPEED_TEST);
    InnerSvcWait(-1);
}

/**
    debug main.
*/
int HttpdDemoMain(int argc, const char* argv[])
{
    uint16_t port = argc > 1 ? (uint16_t)atoi(argv[1]) : 0;
    char line[NORM_BUF_SIZE];

    AppBaseInit(NULL);
    if (HttpdInit(port) < 0) {
        HI_ASSERT(0);
    }

    // 循环接收stdin命令
    while (true) {
        if (!fgets(line, sizeof(line), stdin)) {
            HI_ASSERT(0);
            continue;
        }
        if (strcmp(line, "quit") == 0 || strcmp(line, "exit") == 0) {
            break;
        }
        // 处理命令 ...
    }

    if (HttpdExit() < 0) {
        HI_ASSERT(0);
    }
    AppBaseExit();
    return 0;
}

